/********************************************************************************
  DESCRIPTION OF THE FILE HERE
********************************************************************************/

#include "pool.h"
#include "common.h"

#include <semaphore.h>
#include "request.h"
#include "response.h"
#include "thread_list.h"
#include "logger.h"

typedef struct SPool {  
  ptrThreadList tlist; /* la lista di thread nel pool */
  int size;
  
  /* semaforo che indica se è possibile assegnare un thread del pool oppure
   * se la richiesta dovra' bloccarsi prima di essere servita
   */
  sem_t create_thread;

  ptrLogger logger;
} Pool;

static void Pool_init(ptrPool pool, int size, long long int logging_lapse, char * logfile_name){
  int i;
  pool->size = size;
  pool->tlist = ThreadList_create();
  for (i=0; i<pool->size; i++){
    ThreadList_addNewThread(pool->tlist);
  }
  sem_init(&pool->create_thread, 0, POOL_MAX_SIZE);
  pool->logger = Logger_create(pool, logging_lapse, logfile_name);
  Logger_start(pool->logger);
}

ptrPool Pool_create(int size, long long int logging_lapse, char * logfile_name){
  ptrPool pool = new(Pool);
  Pool_init(pool, size, logging_lapse, logfile_name);
  return pool;
}

/* l'operazione un po' sporca di gestire la connessione qui trova motivo con il
 * fatto che altrimenti si sarebbe dovuto gestire un ulteriore pool di thread
 * che si occupasse dell'invio a mittente delle response.
 */
void Pool_assignSendAndCloseConnection(ptrPool pool, ptrRequest r, int client_sd){
  if (pool == NULL) throw_error("Pool_assignSendAndCloseConnection", "null pool");
  if (r == NULL) throw_error("Pool_assignSendAndCloseConnection", "null request");

  sem_wait(&pool->create_thread);

  ThreadList_assignSendAndCloseConnection(pool->tlist, r, client_sd);

  pool->size = ThreadList_getSize(pool->tlist);
  /* cut off some threads if they are not necessary (not supposed to be) */
  while ((pool->size > POOL_MIN_SIZE) && (pool->size < POOL_MAX_SIZE - 3)){
    ThreadList_cutOffThread(pool->tlist, 3);
  }

  sem_post(&pool->create_thread);
}

void Pool_fprintfStatistics(ptrPool pool, FILE * logfile){
  char * separator = 
    "--------------------------------------------------------------------------------";
  int err, value;
  if (pool == NULL) throw_error("Pool_fprintfStatistics", "null pool");
  if (logfile == NULL) throw_error("Pool_fprintfStatistics", "null log file");

  fprintf(logfile, "\n");
  ThreadList_fprintfStatistics(pool->tlist, logfile);

  /* since the following code is not protected by any mutex it is possible to
   * have a wrong value here.
   */
  err = sem_getvalue(&pool->create_thread, &value);
  /* I don't wanna check err */
  if (value <= 0){
    fprintf(logfile, "\tApproximation of blocked threads: %d\n", (-value));
    fprintf(logfile, "\t(some fraction of time ago this value might be different)\n");
    fprintf(logfile, "%s\n", separator);
  }
  fprintf(logfile, "\n");
}

#ifdef DEBUG
void Pool_printf (ptrPool pool){
  /* BLA, BLA, BLA */
}
#endif /* DEBUG */

static void Pool_deinit(ptrPool pool){
  ThreadList_destroy(pool->tlist);
  sem_destroy(&pool->create_thread);
  Logger_destroy(pool->logger);
};

void Pool_destroy(ptrPool pool){
  if (pool != NULL){
    Pool_deinit(pool);
    delete(pool);
  }
}
